use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use colored::*;
use dialoguer::{Input, MultiSelect, Select};
use git2::{Branch, BranchType, Repository};
use std::fs;
use std::path::PathBuf;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// 拉取并同步分支
    Pull {
        /// 租户代码
        #[arg(short, long)]
        tenant: Option<String>,
    },
    /// 创建新分支
    Create {
        /// 分支名称
        #[arg(short, long)]
        name: String,
        /// 基于的分支
        #[arg(short, long)]
        base: Option<String>,
    },
    /// 推送分支
    Push,
}

struct GitManager {
    repo: Repository,
    config_path: PathBuf,
}

impl GitManager {
    fn new() -> Result<Self> {
        let repo = Repository::open(".")?;
        let config_path = PathBuf::from("sh/pull");
        Ok(Self { repo, config_path })
    }

    fn create_branch(&self, name: &str, base: Option<&str>) -> Result<()> {
        let base_branch = match base {
            Some(base) => base.to_string(),
            None => {
                let branches = self.get_remote_branches()?;
                let selection = Select::new()
                    .with_prompt("选择基础分支")
                    .items(&branches)
                    .interact()?;
                branches[selection].clone()
            }
        };

        println!("{}", format!("基于 {} 创建新分支 {}", base_branch, name).green());

        let base_commit = self.repo
            .find_branch(&base_branch, BranchType::Remote)?
            .get()
            .peel_to_commit()?;

        self.repo.branch(name, &base_commit, false)?;
        println!("{}", "分支创建成功".green());
        Ok(())
    }

    fn pull(&self, tenant: Option<String>) -> Result<()> {
        let current_branch = self.get_current_branch()?;
        let main_branch = self.process_branch_name(&current_branch, tenant)?;

        println!("{}", format!("当前分支: {}", current_branch).green());
        println!("{}", format!("主分支前缀: {}", main_branch).green());

        let remote_branches = self.get_remote_branches()?;
        let matching_branches: Vec<_> = remote_branches
            .iter()
            .filter(|b| b.starts_with(&main_branch))
            .collect();

        let selection = Select::new()
            .with_prompt("选择要合并的远程分支")
            .items(&matching_branches)
            .interact()?;

        let target_branch = &matching_branches[selection];
        self.sync_branches(target_branch)?;
        Ok(())
    }

    fn push(&self) -> Result<()> {
        let current_branch = self.get_current_branch()?;
        println!("{}", format!("推送当前分支: {}", current_branch).green());

        // 执行 git push
        self.repo.find_remote("origin")?
            .push(&[&format!("refs/heads/{}", current_branch)], None)?;

        println!("{}", "推送成功".green());
        Ok(())
    }

    // 辅助方法
    fn get_current_branch(&self) -> Result<String> {
        let head = self.repo.head()?;
        Ok(head.shorthand().unwrap_or("HEAD").to_string())
    }

    fn get_remote_branches(&self) -> Result<Vec<String>> {
        let branches = self.repo.branches(Some(BranchType::Remote))?;
        let mut branch_names = Vec::new();

        for branch in branches {
            let (branch, _) = branch?;
            if let Some(name) = branch.name()? {
                branch_names.push(name.to_string());
            }
        }

        Ok(branch_names)
    }

    fn process_branch_name(&self, branch: &str, tenant: Option<String>) -> Result<String> {
        let mut parts: Vec<&str> = branch.split('@').collect();
        let base = parts[0];

        if let Some(tenant) = tenant {
            Ok(format!("{}.{}", base, tenant))
        } else {
            let parts: Vec<&str> = base.split('.').collect();
            if parts.len() >= 3 {
                Ok(format!("{}.{}.{}", parts[0], parts[1], parts[2]))
            } else {
                Ok(base.to_string())
            }
        }
    }

    fn sync_branches(&self, target: &str) -> Result<()> {
        // 读取配置文件中的分支列表
        let branches = self.read_branch_list()?;

        // 同步操作
        for branch in branches {
            println!("{}", format!("同步分支: {}", branch).blue());

            // 切换到目标分支
            self.repo.set_head(&format!("refs/heads/{}", branch))?;

            // 获取目标分支的引用，并克隆引用而不是移动所有权
            let target_ref = self.repo
                .find_branch(target, BranchType::Remote)?
                .into_reference();

            // 获取目标提交
            let target_oid = target_ref.target()
                .ok_or_else(|| anyhow::anyhow!("无法获取目标分支 OID"))?;
            let target_commit = self.repo.find_commit(target_oid)?;

            // 创建 annotated commit
            let annotated_commit = self.repo.reference_to_annotated_commit(&target_ref)?;

            // 执行合并
            let mut merge_options = git2::MergeOptions::new();
            let mut checkout_options = git2::build::CheckoutBuilder::new();

            self.repo.merge(
                &[&annotated_commit],
                Some(&mut merge_options),
                Some(&mut checkout_options)
            )?;

            // 创建合并提交
            let sig = self.repo.signature()?;
            let tree_id = self.repo.index()?.write_tree()?;
            let tree = self.repo.find_tree(tree_id)?;

            let head = self.repo.head()?;
            let head_commit = self.repo.find_commit(head.target().unwrap())?;

            self.repo.commit(
                Some("HEAD"),
                &sig,
                &sig,
                &format!("auto merge {} to {}", target, branch),
                &tree,
                &[&head_commit, &target_commit]
            )?;

            println!("{}", format!("成功同步 {} 到 {}", target, branch).green());
        }

        Ok(())
    }

    fn read_branch_list(&self) -> Result<Vec<String>> {
        if !self.config_path.exists() {
            return Ok(Vec::new());
        }

        let content = fs::read_to_string(&self.config_path)?;
        Ok(content.lines().map(String::from).collect())
    }
}

fn main() -> Result<()> {
    let cli = Cli::parse();
    let manager = GitManager::new()?;

    match cli.command {
        Commands::Pull { tenant } => {
            manager.pull(tenant)?;
        }
        Commands::Create { name, base } => {
            manager.create_branch(&name, base.as_deref())?;
        }
        Commands::Push => {
            manager.push()?;
        }
    }

    Ok(())
}